package com.tianji.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.json.JSONConfig;
import cn.hutool.json.JSONUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.tianji.api.client.course.CourseClient;
import com.tianji.api.dto.course.CourseSearchDTO;
import com.tianji.api.dto.leanring.LearningLessonDTO;
import com.tianji.api.dto.leanring.LearningRecordDTO;
import com.tianji.api.dto.leanring.LearningRecordFormDTO;
import com.tianji.common.autoconfigure.mq.RabbitMqHelper;
import com.tianji.common.constants.MqConstants;
import com.tianji.common.utils.BeanUtils;
import com.tianji.common.utils.UserContext;
import com.tianji.learning.domain.dto.ComboWriteDTO;
import com.tianji.learning.domain.po.LearningLesson;
import com.tianji.learning.domain.po.LearningRecord;
import com.tianji.learning.mapper.LearningMapper;
import com.tianji.learning.mapper.LearningRecordMapper;
import com.tianji.learning.service.ILearningRecordService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;
import java.time.LocalDateTime;
import java.util.List;
import java.util.stream.Collectors;

/**
 * <p>
 * 学习记录表 服务实现类
 * </p>
 *
 * @author author
 * @since 2024-11-24
 */
@Service
@Slf4j
@RequiredArgsConstructor
public class LearningRecordServiceImpl extends ServiceImpl<LearningRecordMapper, LearningRecord> implements ILearningRecordService {

    private final LearningRecordMapper learningRecordMapper;

    private final LearningMapper learningMapper;

    private final CourseClient courseClient;

    private final RabbitMqHelper rabbitMqHelper;

    private final StringRedisTemplate redisTemplate;

    /**
     * 查询指定课程学习记录
     * @param courseId
     * @return
     */
    @Override
    public LearningLessonDTO getLearningRecords(Long courseId) {
        // 封装学习的课程记录
        // 查询最近学习的小节id
        Long userId = UserContext.getUser();
        LearningLesson learningLesson = queryByUidAndCid(userId, courseId);

        Long latestSectionId = learningLesson.getLatestSectionId();

        // 查询records
        List<LearningRecord> learningRecords = this.lambdaQuery()
                .select(LearningRecord::getSectionId, LearningRecord::getMoment,LearningRecord::getFinished)
                .eq(LearningRecord::getUserId, userId)
                .eq(LearningRecord::getLessonId, learningLesson.getId())
                .list();

        List<LearningRecordDTO> records = learningRecords.stream()
                .map(learningRecord -> BeanUtil.toBean(learningRecord, LearningRecordDTO.class))
                .collect(Collectors.toList());

        return LearningLessonDTO.of(learningLesson.getId(), latestSectionId, records);
    }

    /**
     * 提交学习进度
     * @param learningRecordFormDTO
     */
    @Override
    public void submitStudyRecords(LearningRecordFormDTO learningRecordFormDTO) {
        Long lessonId = learningRecordFormDTO.getLessonId();

        // 查看课程信息
        LearningLesson learningLesson = learningMapper.selectById(lessonId);

        // 查询学习课程对应的信息
        CourseSearchDTO searchInfo = courseClient.getSearchInfo(learningLesson.getCourseId());

        // 提交学习记录
        // 先判断是不是考试
        Integer sectionType = learningRecordFormDTO.getSectionType();
        if(sectionType == 2){
            // 提交的考试
            submitExam(learningRecordFormDTO, searchInfo, learningLesson);

        }else {
            // 提交的是视频
            submitVideo(learningRecordFormDTO, searchInfo, learningLesson);

        }
    }

    /**
     * 提交的是视频
     * @param learningRecordFormDTO
     * @param searchInfo
     * @param learningLesson
     */
    private void submitVideo(LearningRecordFormDTO learningRecordFormDTO, CourseSearchDTO searchInfo, LearningLesson learningLesson) {
        Long userId = UserContext.getUser();

        // 提交的视频
        // 查询redis是否有缓存（根据lessonId）
        // 获取HashValue
        Long lessonId = learningRecordFormDTO.getLessonId();
        String jsonStr = (String) redisTemplate.opsForHash().get(
                "lessonId:" + lessonId.toString(),
                "sectionId:" + learningRecordFormDTO.getSectionId()
        );

        // 如果HashValue无值
        if(jsonStr == null){
            // 从数据库查找同步Redis中
            // 判断数据库是否存在该视频的记录（记录表查）
            LearningRecord learningRecord = this.lambdaQuery()
                    .select(LearningRecord::getFinished, LearningRecord::getMoment, LearningRecord::getId)
                    .eq(LearningRecord::getLessonId, learningRecordFormDTO.getLessonId())
                    .eq(LearningRecord::getSectionId, learningRecordFormDTO.getSectionId())
                    .eq(LearningRecord::getUserId, userId)
                    .one();

            if(ObjectUtil.isNull(learningRecord)){
                // 数据库不存在，直接插入数据库
                BeanUtils.copyProperties(learningRecordFormDTO, learningRecord);
                learningRecord.setUserId(userId);
                learningRecord.setFinished(false);

                learningRecordMapper.insert(learningRecord);

                // 重新赋值
                learningRecord = this.lambdaQuery()
                        .select(LearningRecord::getFinished, LearningRecord::getMoment, LearningRecord::getId)
                        .eq(LearningRecord::getId, learningRecord.getId())
                        .one();

                // 更新LL字段
                updateTbLL(learningRecordFormDTO, learningLesson, searchInfo, null, false);
            }

            // 转成jsonStr
            JSONConfig nullValue = JSONConfig.create().setIgnoreNullValue(true);
            jsonStr = JSONUtil.toJsonStr(learningRecord, nullValue);

            // 存入Redis
            redisTemplate.opsForHash().put(
                    "lessonId:" + lessonId,
                    "sectionId:" + learningRecordFormDTO.getSectionId(),
                    jsonStr);

            return;
        }

        // Redis如果有值
        // 是否学完(已经学习的进度)
        ComboWriteDTO dto = JSONUtil.toBean(jsonStr, ComboWriteDTO.class);
        // 重新封装moment
        dto.setMoment(learningRecordFormDTO.getMoment());
        jsonStr = JSONUtil.toJsonStr(dto);
        // 封装mq的值
        ComboWriteDTO comboWriteDTO = new ComboWriteDTO();
        comboWriteDTO.setLessonId(lessonId);
        comboWriteDTO.setSectionId(learningRecordFormDTO.getSectionId());
        comboWriteDTO.setJsonStr(jsonStr);

        boolean finished = dto.getFinished();
        if(finished){
            // 已经学完的视频
            // 直接写入Redis
            comboWriteDTO.setMoment(learningRecordFormDTO.getMoment());
            redisTemplate.opsForHash().put("lessonId:" + lessonId, "sectionId:" + learningRecordFormDTO.getSectionId(), jsonStr);

            //  死信交换机
            rabbitMqHelper.sendDelayMessage("dl.exchange", "dl.key", comboWriteDTO, Duration.ofSeconds(20));

        }else {
            // 未学完的视频
            boolean flag = (learningRecordFormDTO.getDuration() / 2) < learningRecordFormDTO.getMoment();

            if(flag){
                // 第一次学完了
                // 直接更新db
                this.lambdaUpdate()
                            .set(LearningRecord::getFinished, true)
                            .set(LearningRecord::getFinishTime, LocalDateTime.now())
                            .set(LearningRecord::getMoment, learningRecordFormDTO.getMoment())
                            .eq(LearningRecord::getLessonId, learningRecordFormDTO.getLessonId())
                            .eq(LearningRecord::getSectionId, learningRecordFormDTO.getSectionId())
                            .eq(LearningRecord::getUserId, userId)
                            .update();

                updateTbLL(learningRecordFormDTO, learningLesson, searchInfo, true, true);

                // 删除redis
                redisTemplate.opsForHash().delete("lessonId:" + lessonId, "sectionId:" + learningRecordFormDTO.getSectionId());

                // 学完了增加积分
                rabbitMqHelper.send(
                        MqConstants.Exchange.LEARNING_EXCHANGE,
                        MqConstants.Key.LEARN_SECTION,
                        userId
                );
            }else {
                // 未学完，直接写入redis
                comboWriteDTO.setMoment(learningRecordFormDTO.getMoment());
                redisTemplate.opsForHash().put("lessonId:" + lessonId, "sectionId:" + learningRecordFormDTO.getSectionId(), jsonStr);

                //  死信交换机
                rabbitMqHelper.sendDelayMessage("dl.exchange", "dl.key", comboWriteDTO, Duration.ofSeconds(20));
            }

        }






//        if(!ObjectUtil.isNull(learningRecord)){
//            // 如果存在记录
//            // 判断该记录是否已完成
//            if(!learningRecord.getFinished()){
//                // 视频 判断是否学完一半
//                Boolean flag = (learningRecordFormDTO.getDuration() / 2) < learningRecordFormDTO.getMoment();
//
//                // 更新数据库
//                updateTbLL(learningRecordFormDTO, learningLesson, searchInfo, flag, true);
//
//                if(flag){
//                    // 如果学完，更新记录状态，true
//                    this.lambdaUpdate()
//                            .set(LearningRecord::getFinished, true)
//                            .set(LearningRecord::getFinishTime, LocalDateTime.now())
//                            .set(LearningRecord::getMoment, learningRecordFormDTO.getMoment())
//                            .eq(LearningRecord::getId, learningRecord.getId())
//                            .update();
//                }
//            }else {
//                // 如果存在，且如果学完了（学完过）
//                // 如果存在，且如果未学完（学完过）
//                updateTbLL(learningRecordFormDTO, learningLesson, searchInfo, null, false);
//            }
//
//            // 更新观看进度
//            this.lambdaUpdate()
//                    .set(LearningRecord::getMoment, learningRecordFormDTO.getMoment())
//                    .eq(LearningRecord::getId, learningRecord.getId())
//                    .update();
//
//            return;
//
//        }
//
//        // 如果不存在，则插入（学完的前提是有记录，所以这里不需要判断学完改状态）
//        // 插入课程记录
//         learningRecord = BeanUtil.toBean(learningRecordFormDTO, LearningRecord.class);
//        learningRecord.setUserId(userId);
//        learningRecord.setFinished(false);
//        learningRecord.setFinishTime(LocalDateTime.now());
//        learningRecordMapper.insert(learningRecord);
//
//        // 更新最近学习小节和最近学习时间
//        updateTbLL(learningRecordFormDTO, learningLesson, searchInfo, null, false);

    }



    /**
     * 提交的是考试
     * @param learningRecordFormDTO
     * @param searchInfo
     * @param learningLesson
     */
    private void submitExam(LearningRecordFormDTO learningRecordFormDTO, CourseSearchDTO searchInfo, LearningLesson learningLesson) {
        Long userId = UserContext.getUser();

        // 如果是 说明已经考试完成
        // 学习记录表 新增记录
        LearningRecord learningRecord = BeanUtil.toBean(learningRecordFormDTO, LearningRecord.class);
        learningRecord.setUserId(userId);
        learningRecord.setFinished(true);
        learningRecord.setFinishTime(LocalDateTime.now());

        learningRecordMapper.insert(learningRecord);

        // 更新课表
        updateTbLL(learningRecordFormDTO, learningLesson, searchInfo, null, true);

        // 积分表异步插入数据
        rabbitMqHelper.send(
                MqConstants.Exchange.LEARNING_EXCHANGE,
                MqConstants.Key.LEARN_SECTION,
                userId
        );

    }

    /**
     * 更新LearningLesson表
     * @param learningRecordFormDTO
     * @param learningLesson
     * @param searchInfo
     */
    private void updateTbLL(LearningRecordFormDTO learningRecordFormDTO,
                            LearningLesson learningLesson,
                            CourseSearchDTO searchInfo,
                            Boolean flag,
                            Boolean plusOneOrNot) {


        // 如果未完成，且如果未学完（从未学完），最近学习小节和最近学习时间
        LambdaUpdateWrapper<LearningLesson> wrapper = Wrappers.<LearningLesson>lambdaUpdate()
                .set(LearningLesson::getLatestLearnTime, LocalDateTime.now())
                .set(LearningLesson::getLatestSectionId, learningRecordFormDTO.getSectionId())
                // 如果学完（第一次），状态
                .set(plusOneOrNot ? flag : false, LearningLesson::getLearnedSections, learningLesson.getLearnedSections() + 1)
                // 如果全部小节学完，更新课程状态为2
                .set(plusOneOrNot ? (learningLesson.getLearnedSections()+1 == searchInfo.getSections()) : false,
                        LearningLesson::getStatus,
                        2)
                .eq(LearningLesson::getId, learningRecordFormDTO.getLessonId());

        learningMapper.update(learningLesson, wrapper);
    }

    /**
     * 根据userId和courseId查询学习的课程
     * @param userId
     * @param courseId
     * @return
     */
    private LearningLesson queryByUidAndCid(Long userId, Long courseId) {

        QueryWrapper<LearningLesson> wrapper = Wrappers.<LearningLesson>query()
                .eq("user_id", userId)
                .eq("course_id", courseId);


        LearningLesson learningLesson = learningMapper.selectOne(wrapper);

        return learningLesson;
    }
}
